
import { Vec3 } from "./Vec3";

/**
 * This class provides mathematical operations for internal purposes.
 */
export class MathUtil {
	/**
	 * Positive infinity.
	 */
	public static POSITIVE_INFINITY: number = Number.MAX_SAFE_INTEGER;

	/**
	 * Negative infinity.
	 */
	public static NEGATIVE_INFINITY: number = Number.MIN_SAFE_INTEGER;

	/**
	 * The ratio of the circumference of a circle to its diameter.
	 */
	public static PI: number = 3.14159265358979;

	/**
	 * Shorthand for `PI * 2`.
	 */
	public static TWO_PI: number = MathUtil.PI * 2;

	/**
	 * Shorthand for `PI / 2`.
	 */
	public static HALF_PI: number = MathUtil.PI / 2;

	/**
	 * Shorthand for `PI / 180`.
	 */
	public static TO_RADIANS: number = MathUtil.PI / 180;

	/**
	 * Shorthand for `180 / PI`.
	 */
	public static TO_DEGREES: number = 180 / MathUtil.PI;

	/**
	 * Returns `Math.sin(x)`.
	 */
	public static sin(x: number): number {
		return Math.sin(x);
	}

	/**
	 * Returns `Math.cos(x)`.
	 */
	public static cos(x: number): number {
		return Math.cos(x);
	}

	/**
	 * Returns `Math.tan(x)`.
	 */
	public static tan(x: number): number {
		return Math.tan(x);
	}

	/**
	 * Returns `Math.asin(x)`.
	 */
	public static asin(x: number): number {
		return Math.asin(x);
	}

	/**
	 * Returns `Math.acos(x)`.
	 */
	public static acos(x: number): number {
		return Math.acos(x);
	}

	/**
	 * Returns `Math.atan(x)`.
	 */
	public static atan(x: number): number {
		return Math.atan(x);
	}

	/**
	 * Returns `Math.asin(clamp(-1, x, 1))`.
	 * This never returns `NaN` as long as `x` is not `NaN`.
	 */
	public static safeAsin(x: number): number {
		if (x <= -1) return -MathUtil.HALF_PI;
		if (x >= 1) return MathUtil.HALF_PI;
		return Math.asin(x);
	}

	/**
	 * Returns `Math.acos(clamp(-1, x, 1))`.
	 * This never returns `NaN` as long as `x` is not `NaN`.
	 */
	public static safeAcos(x: number): number {
		if (x <= -1) return MathUtil.PI;
		if (x >= 1) return 0;
		return Math.acos(x);
	}

	/**
	 * Returns `Math.atan2(y, x)`
	 */
	public static atan2(y: number, x: number): number {
		return Math.atan2(y, x);
	}

	/**
	 * Returns `Math.sqrt(x)`.
	 */
	public static sqrt(x: number): number {
		return Math.sqrt(x);
	}

	/**
	 * Returns a clamped value of `x` from `min` to `max`.
	 */
	public static clamp(x: number, min: number, max: number): number {
		return x < min ? min : x > max ? max : x;
	}

	/**
	 * Returns `Math.random()`.
	 */
	public static rand(): number {
		return Math.random();
	}

	/**
	 * Returns a random value from `min` inclusive to `max` exclusive.
	 */
	public static randIn(min: number, max: number): number {
		return min + MathUtil.rand() * (max - min);
	}

	/**
	 * Returns a random `Vec3` from `(min, min, min)` inclusive to `(max, max, max)` exclusive.
	 */
	public static randVec3In(min: number, max: number): Vec3 {
		return new Vec3(min + MathUtil.rand() * (max - min), min + MathUtil.rand() * (max - min), min + MathUtil.rand() * (max - min));
	}

	/**
	 * Returns a random `Vec3` from `(-1.0, -1.0, -1.0)` inclusive to `(1.0, 1.0, 1.0)` exclusive.
	 */
	public static randVec3(): Vec3 {
		return MathUtil.randVec3In(-1, 1);
	}
}
